#include "../config.h"

#include <glibmm/i18n.h>

#include <string.h>

#include "log.h"
#include "strfuncs.h"
#include "application.h"
#include "command.h"
#include "format.h"
#include "yellowpage.h"

namespace gpyp {

//========================
// ID
const char *ID::ID_INVALID = "00000000000000000000000000000000";

void ID::set(const std::string &id_str) {
  if (id_str.size() != LENGTH_STRING) {
    reset();
    return;
  }
  const char *id_cstr = id_str.c_str();
  for(const char *p = id_cstr; *p; ++p) {
    if ((*p < '0' || *p > '9') &&
        (*p < 'A' || *p > 'F') &&
        (*p < 'a' || *p > 'f')) {
      reset();
      return;
    }
  }
  valid_ = false;

#define HEX2CHR(CHR) ((CHR)-((CHR)>='a'&&(CHR)<='f'?'a'-10:((CHR)>='A'&&(CHR)<='F'?'A'-10:'0')))
  for(size_t i = 0; i < LENGTH; ++i) {
    size_t offset = i * LENGTH_STRING_BLOCK;
    block_t block = 0;
    for(size_t j = 0; j < LENGTH_STRING_BLOCK; ++j) {
      block <<= 4;
      block |= HEX2CHR(id_cstr[offset + j]);
    }
    if (block) {
      valid_ = true;
    }
    data_[i] = block;
  }
#undef HEX2CHR
}

//========================
// Channel
#define CH_PROP(NAME, FS, FI)                                           \
  const Channel::Property Channel::_prop_##NAME = {#NAME, FS, FI}
CH_PROP(line,          &Channel::get_line,           NULL);
CH_PROP(rank,          &Channel::get_rank_str,      &Channel::get_rank);
CH_PROP(name,          &Channel::get_name,           NULL);
CH_PROP(id,            &Channel::get_id_str,         NULL);
CH_PROP(tip,           &Channel::get_tip,            NULL);
CH_PROP(contact,       &Channel::get_contact,        NULL);
CH_PROP(genre,         &Channel::get_genre,          NULL);
CH_PROP(desc,          &Channel::get_desc,           NULL);
CH_PROP(listeners,     &Channel::get_listeners_str, &Channel::get_listeners);
CH_PROP(relays,        &Channel::get_relays_str,    &Channel::get_relays);
CH_PROP(bitrate,       &Channel::get_bitrate_str,   &Channel::get_bitrate);
CH_PROP(type,          &Channel::get_type,           NULL);
CH_PROP(track_artist,  &Channel::get_track_artist,   NULL);
CH_PROP(track_album,   &Channel::get_track_album,    NULL);
CH_PROP(track_title,   &Channel::get_track_title,    NULL);
CH_PROP(track_contact, &Channel::get_track_contact,  NULL);
CH_PROP(name_url,      &Channel::get_name_url,       NULL);
CH_PROP(age,           &Channel::get_age_str,       &Channel::get_age);
CH_PROP(status,        &Channel::get_status,         NULL);
CH_PROP(comment,       &Channel::get_comment,        NULL);
CH_PROP(ypname,        &Channel::get_ypname,         NULL);
CH_PROP(ypurl,         &Channel::get_ypurl,          NULL);
const Channel::Property *Channel::_properties[] = {
  &_prop_line, &_prop_rank, &_prop_name, &_prop_id, &_prop_tip,
  &_prop_contact, &_prop_genre, &_prop_desc, &_prop_listeners,
  &_prop_relays, &_prop_bitrate, &_prop_type, &_prop_track_artist,
  &_prop_track_album, &_prop_track_title, &_prop_track_contact,
  &_prop_name_url, &_prop_age, &_prop_status, &_prop_comment,
  &_prop_ypname, &_prop_ypurl, NULL
};

const Channel::Property *Channel::get_property(const Glib::ustring& name) {
  for(const Property **prop = _properties; *prop; ++prop) {
    if (name == (*prop)->name) {
      return *prop;
    }
  }
  return NULL;
}

const char   *Channel::INVALIDINTVALUE = "-";

const Glib::ustring &Channel::get_ypname() const {
  if (yp_) {
    return *yp_->name();
  } else {
    return __dummy_string__;
  }
}
const Glib::ustring &Channel::get_ypurl() const {
  if (yp_) {
    return *yp_->url();
  } else {
    return __dummy_string__;
  }
}

void Channel::rematch_favorite() {
  match_.reset(new FavoriteMatch(app_.favcollection(), *this));
}

void Channel::set_previous(RefChannel ch) {
  if (ch) {
    set_is_new(false);
    set_is_changed(name_         != ch->name_ ||
                   genre_        != ch->genre_ ||
                   desc_         != ch->desc_ ||
                   comment_      != ch->comment_ ||
                   track_artist_ != ch->track_artist_ ||
                   track_title_  != ch->track_title_ ||
                   track_album_  != ch->track_album_);
    time_last_played_ = ch->time_last_played_;
    set_is_auto_played(ch->get_is_auto_played());
  }
}

Channel::Channel(Application &app, YellowPage *yp, int rank) :
  app_         (app),
  yp_          (yp),
  time_created_(boost::posix_time::second_clock::local_time()),
  rank_        (rank),
  rank_str_    (tostr(rank_)),
  listeners_   (-1),
  relays_      (-1),
  bitrate_     (-1),
  age_         (-1),
  age_str_     ("--:--"),
  flags_       (FLAG_NEW)
{
}

//========================
// YPParser
void YPParser::add_channel(RefChannel ch) {
  ch->rematch_favorite();
  channel_list_.push_back(ch);

  if (hash_table_.lookup(ch->get_id())) {
    log::err << *yp_.name() << ": Channel ID duplicated - "
             << ch->get_name() << " " << ch->get_id() << log::endl;
  } else {
    hash_table_.insert(ch->get_id(), ch);
  }
}

YPParser::YPParser(Application           &app,
                   YellowPage            &yp,
                   std::list<RefChannel> &channel_list,
                   IDHashTable<Channel>  &hash_table) :
  app_         (app),
  yp_          (yp),
  channel_list_(channel_list),
  hash_table_  (hash_table)
{
  channel_list_.clear();
  hash_table_.clear();
}
YPParser::~YPParser() {
}

//========================
// YPTXTParser
const YPTXTParser::Column YPTXTParser::_columns[] = {
  &Channel::name_,
  &Channel::id_str_,
  &Channel::tip_,
  &Channel::contact_,
  &Channel::genre_,
  &Channel::desc_,
  &Channel::listeners_str_,
  &Channel::relays_str_,
  &Channel::bitrate_str_,
  &Channel::type_,
  &Channel::track_artist_,
  &Channel::track_album_,
  &Channel::track_title_,
  &Channel::track_contact_,
  &Channel::name_url_,
  &Channel::age_str_,
  &Channel::status_,
  &Channel::comment_,
   NULL
};

const char YPTXTParser::SPLITTER[] = "<>";

void YPTXTParser::parse(const conf::Source &source) {
  const char *data = source.data();
  size_t      size = source.size();
  const char *p_s = data, *end = data + size;
  for(int rank = 1; p_s < end; ++rank) {
    const char *p_e;
    size_t column_count = 1;
    for(p_e = p_s; p_e < end && *p_e != '\r' && *p_e != '\n'; ++p_e) {
      if (strncmp(p_e, SPLITTER, sizeof(SPLITTER) - 1) == 0) ++column_count;
    }
    if (p_e > p_s && column_count > 3) {
      Channel *ch = new Channel(app_, &yp_, rank);
      ch->line_.assign(p_s, p_e);

      const char *pos = ch->line_.c_str(), *splitpos = NULL;
      for(const Column* column = _columns; *column; ++column) {
        splitpos = strstr(pos, SPLITTER);
        decode_html(pos, splitpos, ch->*(*column));
        if (!splitpos) break;
        pos = splitpos + sizeof(SPLITTER) - 1;
      }

      ch->id_.set(ch->id_str_);

#define CH_SET_INT(NAME) {                                              \
        ch->NAME##_ = toint(ch->NAME##_str_, -1);                       \
        if (ch->NAME##_ < 0) ch->NAME##_str_ = Channel::INVALIDINTVALUE; \
      }
      CH_SET_INT(listeners);
      CH_SET_INT(relays);
      CH_SET_INT(bitrate);
#undef CH_SET_INT

      Glib::ustring::size_type agesplitpos = ch->age_str_.find(':');
      if (agesplitpos == Glib::ustring::npos) {
        ch->age_ = ch->age_str_.empty() ? -1 : toint(ch->age_str_, -1);
      } else {
        Glib::ustring hour(ch->age_str_, 0, agesplitpos);
        Glib::ustring min (ch->age_str_, agesplitpos + 1);
        ch->age_ = toint(hour, 0) * 60 + toint(min, 0);
      }

      add_channel(RefChannel(ch));
    }
    for(p_s = p_e; p_s < end && (*p_s == '\r' || *p_s == '\n'); ++p_s) ;
  }
}

YPTXTParser::YPTXTParser(Application           &app,
                         YellowPage            &yp,
                         std::list<RefChannel> &channel_list,
                         IDHashTable<Channel>  &hash_table) :
  YPParser(app, yp, channel_list, hash_table)
{
}

//========================
// YPXMLParser
void YPXMLParser::on_start_parsing() {
  YPXMLParserBase::on_start_parsing();
  total_ = -1;
}
void YPXMLParser::on_error(GError *error) {
  log::err << "YPXMLParser: error";
  if (error && error->message) {
    log::err << " - " << error->message;
  }
  log::err << log::endl;
}

void YPXMLParser::onload_chs(const AttributesWrapper &attributes) {
  total_ = attributes.get("total", -1);
  channel_list_.clear();
  hash_table_  .clear();
}
void YPXMLParser::onload_chs_ch(const AttributesWrapper &/*attributes*/) {
  ch_.reset(new Channel(app_, &yp_, channel_list_.size() + 1));
}
void YPXMLParser::unload_chs_ch() {
  add_channel(ch_);
  ch_.reset();
}

YPXMLParser::YPXMLParser(Application           &app,
                         YellowPage            &yp,
                         bool                   peercast,
                         std::list<RefChannel> &channel_list,
                         IDHashTable<Channel>  &hash_table) :
  YPParser(app, yp, channel_list, hash_table),
  total_  (-1)
{
  Context *c = NULL;
  c = add("peercast");
  c = c->add(peercast ? "channels_relayed" : "channels_found",
             sigc::mem_fun(*this, &YPXMLParser::onload_chs));
  c = c->add("channel",
             sigc::mem_fun(*this, &YPXMLParser::onload_chs_ch),
             sigc::mem_fun(*this, &YPXMLParser::unload_chs_ch));
  setup_handler_channel_attr(*c);
  setup_handler_track_attr(*c->add("track"));
  setup_handler_relay_attr(*c->add("relay"));
}

//========================
// AttributeYPType
AttributeYPType::NameTable AttributeYPType::_name_table[] = {
  {YPTYPE_YP4G,     "yp4g"},
  {YPTYPE_XML,      "xml"},
  {YPTYPE_XML_PC,   "xml_pc"},
  {YPTYPE_INVALID,  NULL}
};

//========================
// YellowPage
void YellowPage::reset_reload_timeout() {
  conn_reload_timeout_.disconnect();
  size_t timeout = *reload_interval_ * 60;
  if (*enable_ && timeout) {
    ConfMain::RefNetwork net = app_.conf().network();
    if (error_count_ && error_count_ <= *net->autoretry_limit()) {
      timeout = *net->autoretry_interval();
    }
    timeout *= 1000;
    conn_reload_timeout_ = Glib::signal_timeout()
      .connect(sigc::mem_fun(*this, &YellowPage::on_reload_timeout), timeout);
  }
}
bool YellowPage::on_reload_timeout() {
  reload();
  return false;
}

void YellowPage::set_status(Status status) {
  if (status != status_) {
    Status old = status_;
    status_ = status;
    signal_status_changed_(*this, status_, old);
  }
}

namespace {
class CreateEnded : boost::noncopyable {
private:
  IDHashTable<Channel>     new_hash_table_;
  int                      time_;
  boost::posix_time::ptime now_;

public:
  inline void move(const ID &id, RefChannel ch) {
    const boost::posix_time::ptime &time = ch->get_time_ended();
    if (time.is_not_a_date_time()) return;
    if ((now_ - time).total_seconds() < time_) {
      new_hash_table_.insert(id, ch);
    }
  }
  inline void add(const ID &id, RefChannel ch) {
    ch->set_is_ended(true);
    new_hash_table_.insert(id, ch);
    log::log << "Ended - " << ch->get_name() << log::endl;
  }

  inline IDHashTable<Channel> &operator*() {
    return new_hash_table_;
  }
  inline IDHashTable<Channel> &operator->() {
    return new_hash_table_;
  }

  inline CreateEnded(Application &app) :
    time_(*app.ypcollection()->time_keep_ended() * 60 * 60),
    now_ (boost::posix_time::second_clock::local_time())
  {
  }
};
}
bool YellowPage::create_channels(const conf::Source &source) {
  if (!source) {
    error_message_ = _("Data empty");
    return false;
  }

  ChannelList new_channel_list;
  IDHashTable<Channel> new_channel_hash;
  try {
    bool xml_pc = false;
    switch(*type_) {
    case YPTYPE_YP4G:
      {
        YPTXTParser parser(app_, *this, new_channel_list, new_channel_hash);
        parser.parse(source);
      }
      break;
    case YPTYPE_XML_PC:
      xml_pc = true;
    case YPTYPE_XML:
      {
        YPXMLParser parser(app_, *this, xml_pc,
                           new_channel_list, new_channel_hash);
        if (!parser.parse(source)) {
          error_message_ = _("failed to parse XML");
          return false;
        }
        if (parser.total() >= 0 &&
            static_cast<unsigned>(parser.total()) != new_channel_list.size()) {
          log::err << *name_ << ": channel count mismatch" << log::endl;
        }
      }
      break;
    default:
      error_message_ = _("Unimplemented yptype");
      return false;
    }
    for(ChannelList::iterator iter = new_channel_list.begin();
        iter != new_channel_list.end(); ++iter) {
      RefChannel ch = *iter;
      const ID & id = ch->get_id();
      if (id) {
        RefChannel prev = channel_hash_.lookup(id);
        if (prev) {
          ch->set_previous(prev);
          channel_hash_.erase(id);
        } else {
          prev = channel_hash_ended_.lookup(id);
          if (prev) {
            prev->set_is_ended(false);
            ch->set_previous(prev);
            ch->set_is_returned(true);
            channel_hash_ended_.erase(id);
            //log::log << format(_("Returned - %s"), ch->get_name().c_str())
            //         << log::endl;
          }
        }
      }
    }
  } catch(const std::exception& ex) {
    error_message_ = ex.what();
    log::err << *name_ << ": " << error_message_ << log::endl;
    return false;
  }

  CreateEnded ended(app_);
  channel_hash_ended_.foreach(sigc::mem_fun(ended, &CreateEnded::move));
  channel_hash_.foreach(sigc::mem_fun(ended, &CreateEnded::add));
  channel_hash_ended_.swap(*ended);

  channel_list_.swap(new_channel_list);
  channel_hash_.swap(new_channel_hash);

  chcount_          = channel_list_.size();
  chcount_valid_    = channel_hash_.size();
  last_loaded_time_ = boost::posix_time::second_clock::local_time();

  new_channel_list.clear();
  new_channel_hash.clear();
  return true;
}

void YellowPage::loader_on_complete(const loader::RefResponse response) {
  loader_conn_.reset();
  error_count_ = 0;
  reset_reload_timeout();
  if (create_channels(response->get_source())) {
    save_cache(response->get_source());
    set_status(STATUS_COMPLETE);

    log::log << "Complete:"
             << " " << *name_
             << " " << chcount_
             << " " << chcount_valid_
             << log::endl;
  } else {
    set_status(STATUS_ERROR);
  }
}
void YellowPage::loader_on_error(const loader::RefError error) {
  log::err << *name_ << ": " << error->get_message() << log::endl;
  loader_conn_.reset();
  ++error_count_;
  reset_reload_timeout();
  error_message_ = error->get_message();
  set_status(STATUS_ERROR);
}

Glib::ustring YellowPage::create_cache_path() {
  return Glib::build_filename(cache_dir_, URL::encode(URL::get_dir(*url_)));
}
bool YellowPage::load_cache() {
  if (!*app_.ypcollection()->use_cache()) {
    error_message_ = _("Cache disabled");
    return false;
  }
  try {
    std::string path = Glib::filename_from_utf8(create_cache_path());
    if (Glib::file_test(path, Glib::FILE_TEST_IS_REGULAR)) {
      bool    res = false;
      char   *data = NULL;
      gsize   length;
      GError *err = NULL;
      if (g_file_get_contents(path.c_str(), &data, &length, &err)) {
        conf::Source source;
        if (source.set(data, length, NULL, error_message_)) {
          if (create_channels(source)) {
            for(ChannelList::iterator iter = channel_list_.begin();
                iter != channel_list_.end(); ++iter) {
              (*iter)->set_is_notified(true);
            }
            res = true;
          }
        }
      } else {
        if (err && err->message) {
          error_message_ = err->message;
        } else {
          error_message_ = _("Unknown error");
        }
      }
      if (data) g_free(data);
      if (err)  g_error_free(err);
      return res;
    } else {
      return true;
    }
  } catch(const Glib::Exception &ex) {
    log::err << *name_ << ": " << ex.what() << log::endl;
    return false;
  } catch(const InvalidURL &ex) {
    log::err << *name_ << ": " << ex.what() << log::endl;
    return false;
  }
}
void YellowPage::save_cache(const conf::Source &source) {
  if (*app_.ypcollection()->use_cache() && source) {
    try {
      std::string dir = Glib::filename_from_utf8(cache_dir_);
      if (!Glib::file_test(dir, Glib::FILE_TEST_EXISTS)) {
        if (g_mkdir_with_parents(dir.c_str(), 0700)) {
          log::err << "failed to create directory - "
                   << dir << log::endl;
          return;
        }
      } else if (!Glib::file_test(dir, Glib::FILE_TEST_IS_DIR)) {
        log::err << dir << " is not directory" << log::endl;
        return;
      }
      std::string path = Glib::filename_from_utf8(create_cache_path());
      g_file_set_contents(path.c_str(), source.data(), source.size(), NULL);
    } catch(const Glib::Exception &ex) {
      log::err << *name_ << ": " << ex.what() << log::endl;
    } catch(const InvalidURL &ex) {
      log::err << *name_ << ": " << ex.what() << log::endl;
    }
  }
}

void YellowPage::reload() {
  if (*enable_) {
    set_status(STATUS_LOADING);
    try {
      URL url(*url_);
      switch(*type_) {
      case YPTYPE_YP4G:
        if (url.is_dir()) url.add_path("index.txt", false);
        url.set_param("host", *app_.conf().network()->peercast_server());
        break;
      case YPTYPE_XML:
        url.set_param("xml");
        break;
      case YPTYPE_XML_PC:
        if (url.is_dir()) url.add_path("admin");
        url.set_param("cmd", "viewxml");
        break;
      default:
        break;
      }
      if (loader_conn_) loader_conn_->cancel();

      if (*pcraw_proxy_) {
        URL url_pc(Glib::ustring("http://") +
                   *app_.conf().network()->pcraw_proxy_server() +
                   "/" + url.to_string());
        loader_conn_ = loader_.req(url_pc, *encoding_);
        loader_conn_->load();
      } else {
        loader_conn_ = loader_.req(url, *encoding_);
        loader_conn_->load();
      }
    } catch(const InvalidURL &ex) {
      error_message_ = ex.what();
      log::err << *name_ << ": " << error_message_ << log::endl;
      set_status(STATUS_ERROR);
    }
  } else {
    reset_reload_timeout();
  }
}
void YellowPage::rematch_favorite() {
  for(iterator iter = begin(); iter != end(); ++iter) {
    (*iter)->rematch_favorite();
  }
}

YellowPage::YellowPage(const Glib::ustring &name,
                       Application         &app,
                       const Glib::ustring &cache_dir) :
  conf::Element     (name),
  app_              (app),
  cache_dir_        (cache_dir),
  enable_           ("enable",          true),
  icon_             ("icon",            ""),
  name_             ("name",            "YP"),
  url_              ("url",             "http://host/path/"),
  encoding_         ("encoding",        ""),
  type_             ("type",            YPTYPE_YP4G),
  pcraw_proxy_      ("pcraw_proxy",     false),
  reload_interval_  ("reload_interval", 10),
  status_           (STATUS_NONE),
  loader_           (app_),
  chcount_          (0),
  chcount_valid_    (0),
  error_count_      (0)
{
  add(enable_);
  add(icon_);
  add(name_);
  add(url_);
  add(encoding_);
  add(type_);
  add(pcraw_proxy_);
  add(reload_interval_);

  loader_.signal_complete()
    .connect(sigc::mem_fun(*this, &YellowPage::loader_on_complete));
  loader_.signal_error()
    .connect(sigc::mem_fun(*this, &YellowPage::loader_on_error));

  reset_reload_timeout();
  enable_->signal_changed()
    .connect(sigc::mem_fun(*this, &YellowPage::reset_reload_timeout));
  reload_interval_->signal_changed()
    .connect(sigc::mem_fun(*this, &YellowPage::reset_reload_timeout));
}
YellowPage::~YellowPage() {
  conn_reload_timeout_.disconnect();
  if (loader_conn_) loader_conn_->cancel();
}

//========================
// YPCollection
RefYellowPage YPCollection::on_create_item(const Glib::ustring &name) {
  RefYellowPage yp(new YellowPage(name, app_, cache_dir_));
  yp->signal_status_changed()
    .connect(sigc::mem_fun(*this, &YPCollection::yp_on_status_changed));
  return yp;
}
void YPCollection::on_load_default() {
  conf::Collection<YellowPage, YPCollection>::on_load_default();
  add_item("CP",  "http://cp.webhop.net/",                IconManager::CP);
  //add_item("XP",  "http://extremeyp.ath.cx/",             IconManager::XP);
  add_item("TP",  "http://temp.orz.hm/yp/",               IconManager::YP4G);
  add_item("RP",  "http://retro.servegame.org/",          IconManager::RP);
  add_item("SP",  "http://bayonet.aa0.netvolante.jp/sp/", IconManager::SP);
  add_item("YPM", "http://cp.webhop.net/ypm/",            IconManager::CP);
  add_item("YP",  "http://yp.peercast.org/",              IconManager::PEERCAST, YPTYPE_XML, false);
}

void YPCollection::update_chcount() {
  chcount_ = chcount_valid_ = 0;
  for(iterator iter = begin(); iter != end(); ++iter) {
    if (*(*iter)->enable()) {
      chcount_       += (*iter)->get_chcount();
      chcount_valid_ += (*iter)->get_chcount_valid();
    }
  }
}
void YPCollection::yp_on_status_changed(YellowPage &yp,
                                        YellowPage::Status new_status,
                                        YellowPage::Status old_status) {
  if (new_status == YellowPage::STATUS_LOADING) {
    if (old_status != YellowPage::STATUS_LOADING) {
      ++loading_count_;
      signal_loading_count_changed_(loading_count_);
    }
  } else if (old_status == YellowPage::STATUS_LOADING) {
    --loading_count_;
    signal_loading_count_changed_(loading_count_);
  }
  update_chcount();
  if (new_status == YellowPage::STATUS_ERROR) {
    signal_error_(yp);
  }
  if (loading_count_ == 0) {
    signal_loaded_();
  }
}

void YPCollection::load_cache() {
  if (*use_cache_) {
    for(iterator iter = begin(); iter != end(); ++iter) {
      if (*(*iter)->enable()) {
        if (!(*iter)->load_cache()) {
          log::err << (*iter)->get_error_message() << log::endl;
        }
      }
    }
    update_chcount();
    signal_loaded_();
  }
}

RefYellowPage YPCollection::find(const Glib::ustring &name) {
  for(iterator iter = begin(); iter != end(); ++iter) {
    if (*(*iter)->name() == name) {
      return *iter;
    }
  }
  return RefYellowPage();
}
RefYellowPage YPCollection::find(const YellowPage *yp) {
  if (yp) {
    for(iterator iter = begin(); iter != end(); ++iter) {
      if (iter->get() == yp) {
        return *iter;
      }
    }
  }
  return RefYellowPage();
}
RefChannel YPCollection::find(const ID &id) {
  if (id) {
    for(iterator iter = begin(); iter != end(); ++iter) {
      RefChannel ch = (*iter)->find(id);
      if (ch) return ch;
    }
  }
  return RefChannel();
}
RefChannel YPCollection::find_ended(const ID &id) {
  if (id) {
    for(iterator iter = begin(); iter != end(); ++iter) {
      RefChannel ch = (*iter)->find_ended(id);
      if (ch) return ch;
    }
  }
  return RefChannel();
}

void YPCollection::rematch_favorite() {
  for(iterator iter = begin(); iter != end(); ++iter) {
    (*iter)->rematch_favorite();
  }
}

void YPCollection::clear_ended() {
  for(iterator iter = begin(); iter != end(); ++iter) {
    IDHashTable<Channel> hash_table;
    (*iter)->get_ended().swap(hash_table);
  }
}

bool YPCollection::bc_yp_reload_all(ShellLine &/*line*/, CommandOpts &/*opts*/) {
  reload_all();
  return true;
}
bool YPCollection::bc_yp_reload(ShellLine &/*line*/, CommandOpts &opts) {
  if (opts.yp) {
    opts.yp->reload();
    return true;
  }
  return false;
}

bool YPCollection::bc_ch_reloadyp(ShellLine &/*line*/, CommandOpts &opts) {
  if (opts.ch) {
    RefYellowPage yp = find(opts.ch->get_yp());
    if (yp) {
      yp->reload();
      return true;
    }
  }
  return false;
}

YPCollection::YPCollection(Application &app) :
  conf::Collection<YellowPage, YPCollection>("yellowpages", "yellowpage"),
  app_            (app),
  cache_dir_      (Glib::build_filename(app_.conf().get_dir_path(),
                                        "ypcache")),
  time_keep_ended_("time_keep_ended", 3),
  use_cache_      ("use_cache", false),
  loading_count_  (0),
  chcount_        (0),
  chcount_valid_  (0),
  bc_yp_          (*app_.bc().add_ns("YP")),
  bc_ch_          (*app_.bc().add_ns("Channel"))
{
  add(time_keep_ended_);
  add(use_cache_);

#define ADD_CMD(NS, CMD, H)                                             \
  bc_##NS##_.add_cmd(#CMD, sigc::mem_fun(*this, &YPCollection::bc_##NS##_##CMD), H)

  ADD_CMD(yp, reload_all, _("Reload all yellowpages"));
  ADD_CMD(yp, reload,     _("Reload yellowpage"));
  ADD_CMD(ch, reloadyp,   _("Reload yellowpage"));
}

}/*gpyp*/
